package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.NoProcessing;
import com.zzyl.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    /**
     * 根据用户id查询对应的资源数据
     *
     * @param userId
     * @return
     */
    @Override
    public List<MenuVo> menus(Long userId) {
        //查询数据
        List<MenuVo> menuVoList = resourceMapper.selectMenuByUserId(userId);
        //构建 parentNo -> List<MenuVo> 的映射
        Map<String, List<MenuVo>> parentMap = menuVoList.stream().collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        //递归构建我的菜单信息
        buildTree(menuVoList, parentMap);
        return parentMap.get(SuperConstant.ROOT_RESOURCE_PARENT_ID);
    }

    /**
     * 递归构建我的菜单信息（已提供）
     * 递归构建我的菜单信息（已提供）
     * 递归构建我的菜单信息（已提供）
     */
    private void buildTree(List<MenuVo> menuVoList, Map<String, List<MenuVo>> parentMap) {
        menuVoList.forEach(menuVo -> {
            //补全数据
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());
            menuVo.setRedirect("/" + menuVo.getName());
            //根据当前资源编号查询子资源
            List<MenuVo> childrenList = parentMap.get(menuVo.getResourceNo());
            if (CollUtil.isNotEmpty(childrenList)) {
                buildTree(childrenList, parentMap);
                menuVo.setChildren(childrenList);
            }
        });
    }

    @Override
    public List<ResourceVo> findList(ResourceDto resourceDto) {
        //1 参数处理
        //1.1 如果用户没有设置查询的数据状态,默认查询可用状态菜单
//        if (resourceDto.getDataState() == null) {
//            resourceDto.setDataState(SuperConstant.DATA_STATE_0);
//        }
        //1.2 如果客户端传入了父菜单id,查询父菜单下的子菜单,需要处理父菜单id,除去后面的0
        if (StrUtil.isNotEmpty(resourceDto.getParentResourceNo())) {
            resourceDto.setParentResourceNo(NoProcessing.processString(resourceDto.getParentResourceNo()));
        }
        //2 调用数据访问层查询菜单
        List<Resource> resourceList = resourceMapper.findList(resourceDto.getParentResourceNo(), resourceDto.getResourceType(), resourceDto.getDataState());
        return BeanUtil.copyToList(resourceList, ResourceVo.class);
    }

    @Override
    public TreeVo findTree(ResourceDto resourceDto) {
        //1. 查询所有的资源菜单
        List<Resource> resourceList = resourceMapper.findList(NoProcessing.processString(SuperConstant.ROOT_RESOURCE_PARENT_ID), SuperConstant.MENU, SuperConstant.DATA_STATE_0);
        //如果没有资源 , 直接返回空集合
        if (CollUtil.isEmpty(resourceList)) {
            return new TreeVo(Collections.emptyList());
        }
        //2. 存在资源解析树形结构
        Optional<Resource> optional = resourceList.stream().filter(resource ->
                StrUtil.equals(resource.getResourceNo(), SuperConstant.ROOT_RESOURCE_PARENT_ID)
        ).findFirst();
        //2.1 获取跟节点
        Resource root = null;
        if (optional.isPresent()) {
            root = optional.get();
        } else {
            root = new Resource();
            root.setResourceNo(SuperConstant.ROOT_RESOURCE_PARENT_ID);
            root.setResourceName("智慧养老院");
        }
        List<TreeItemVo> treeItemVoList = new ArrayList<>();
        // 递归构建树型结构
        recursionResourceList(root, treeItemVoList, resourceList);
        return new TreeVo(treeItemVoList);
    }

    private void recursionResourceList(Resource root, List<TreeItemVo> treeItemVoList, List<Resource> resourceList) {
        //1. 将当前节点构建成一个TreeItemVo类型
        TreeItemVo treeItemVo = TreeItemVo.builder()
                .id(root.getResourceNo())
                .label(root.getResourceName())
                .build();
        //2. 查询当前资源的子资源
        List<Resource> childrenList = resourceList.stream().filter(resource ->
                StrUtil.equals(resource.getParentResourceNo(), root.getResourceNo())
        ).collect(Collectors.toList());
        //判断当前节点是否存在子节点,如果存在,需要找子节点的子节点
        if (CollectionUtil.isNotEmpty(childrenList)) {
            //定义集合,存储子节点数据
            List<TreeItemVo> children = new ArrayList<>();
            childrenList.forEach(resource -> {
                recursionResourceList(resource, children, resourceList);
            });
            treeItemVo.setChildren(children);
        }
        treeItemVoList.add(treeItemVo);
    }

    @Override
    public void addResource(ResourceDto resourceDto) {
        //数据类型转化
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        // 查询父资源
        Resource parentResource = resourceMapper.selectById(resourceDto.getParentResourceNo());
        if (parentResource == null) {
            throw new BaseException(BasicEnum.PARAMETER_MISSING);
        }
        //设置子菜单和父菜单状态一致
        resource.setDataState(parentResource.getDataState());
        // 判断是否是按钮
        boolean isButton = StrUtil.equals(parentResource.getResourceType(), SuperConstant.BUTTON);
        // 父菜单层级不能超过3级
        if (!isButton && NoProcessing.processString(parentResource.getResourceNo()).length() >= 15) {
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }
        // 判断当前父节点, 有无子节点(无子节点, 在父节点的基础上新增即可 , 有子节点需要再最大的子节点基础上增加)
        List<Resource> childResourceList = resourceMapper.findChildResourceByParentResourceNo(parentResource.getResourceNo());
        String no = null;
        if (CollectionUtil.isEmpty(childResourceList)) {
            no = NoProcessing.createNo(parentResource.getResourceNo(), false);
        } else {
            String max = childResourceList.stream().map(childResource ->
                    Long.valueOf(childResource.getResourceNo())
            ).max(Comparator.comparing(id -> id)).get().toString();
            no = NoProcessing.createNo(max, true);
        }
        resource.setResourceNo(no);
        resourceMapper.insert(resource);
    }

    /**
     * 修改资源
     *
     * @param resourceDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(cacheNames = "RESOURCE::List",allEntries = true),
            @CacheEvict(cacheNames = "RESOURCE::TREE",allEntries = true),
            @CacheEvict(cacheNames = "RESOURCE::MENUS",allEntries = true)
    })
    public void updateResource(ResourceDto resourceDto) {
        //1. 查询菜单详情
        Resource resource = resourceMapper.findById(resourceDto.getId());
        if (resource == null) {
            throw new BaseException(BasicEnum.DATA_NOT_EXITS);
        }
        //2. 判断用户是否修改了父菜单
        //2.1 如果没有修改父菜单, 直接更新数据库就可以了
        if (StrUtil.equals(resource.getParentResourceNo(), resourceDto.getResourceNo())) {
            resource.setResourceName(resourceDto.getResourceName());
            resource.setRequestPath(resourceDto.getRequestPath());
            resource.setSortNo(resourceDto.getSortNo());
            resourceMapper.update(resource);
            return;
        }
        //2.2 如果用户修改了父菜单 , 需要调整层级和菜单编号
        //生成新的编号
        String resourceNo = createResourceNo(resourceDto.getParentResourceNo());
        // 如果新的菜单层级大于3级 , 则直接返回错误信息
        if (StrUtil.equals(resource.getResourceType(), SuperConstant.MENU)) {
            if (resourceNo.length() / 3 > 5) {
                throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
            }
        }
        //调整子菜单层级和编号(将旧的父菜单编号替换成新的, 旧的菜单编号替换成新的)
        List<Resource> childList = resourceMapper.findList(NoProcessing.processString(resource.getResourceNo()), null, null);
        // 过滤掉自己(自己的菜单编号已经重新生成 , 只需要调整子就可以了)
        childList = childList.stream().filter(child -> !StrUtil.equals(child.getResourceNo(), resource.getResourceNo())).collect(Collectors.toList());
        //将旧的父菜单编号替换为新的父菜单编号即可
        childList.stream().forEach(child -> {
            // 可以将资源编号看成是两部分 : 父菜单编号部分 + 子菜单编号部分(除去原来的父菜单编号部分剩下的就是自己的)
            // 为子菜单生成新的父菜单编号 : 新的父菜单编号 + 子菜单父编号部分
            String parentResourceNo = NoProcessing.processString(resourceNo) + child.getParentResourceNo().replace(NoProcessing.processString(resource.getResourceNo()), "");
            // 为子菜单生成新的单编号 : 新的父菜单编号 + 子菜单编号部分
            String n_resourceNo = NoProcessing.processString(resourceNo) + child.getResourceNo().replace(NoProcessing.processString(resource.getResourceNo()), "");
            // 处理完成后长度可能超过15,去除后面多余的0
            // 例如 : 现有菜单编号 : 100001001001000   这个菜单之前父菜单编号 100001001000000   调整到 100001001001000下
            // 则按照规则得到的新编号为 :   100001001001001000
            parentResourceNo = NoProcessing.processString(parentResourceNo);
            n_resourceNo = NoProcessing.processString(n_resourceNo);
            //去除零之后长度不满15 , 后面补0
            if (parentResourceNo.length() < 15) {
                parentResourceNo = StrUtil.fillAfter(parentResourceNo, '0', 15);
            }
            if (n_resourceNo.length() < 15) {
                n_resourceNo = StrUtil.fillAfter(n_resourceNo, '0', 15);
            }
            // 如果处理完成后菜单的层级超过3级 , 返回错误信息
            if (StrUtil.equals(child.getResourceType(), SuperConstant.MENU)) {
                if (n_resourceNo.length() / 3 > 5) {
                    throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
                }
            } else {
                n_resourceNo = parentResourceNo + StrUtil.subSuf(n_resourceNo, n_resourceNo.length() - 3);
            }
            // 为子菜单设置最新的资源id和父资源id
            child.setResourceNo(n_resourceNo);
            child.setParentResourceNo(parentResourceNo);
        });
        //3. 修改菜单数据和子菜单数据
        // 更新父菜单数据
        resource.setResourceName(resourceDto.getResourceName());
        resource.setRequestPath(resourceDto.getRequestPath());
        resource.setSortNo(resourceDto.getSortNo());
        resource.setParentResourceNo(resourceDto.getParentResourceNo());
        resource.setResourceNo(resourceNo);
        resourceMapper.update(resource);
        // 跟新子菜单数据
        childList.stream().forEach(child -> resourceMapper.update(child));
    }

    @Override
    public void updateResourceDataState(ResourceDto resourceDto) {
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        Resource parentResource = resourceMapper.selectById(resourceDto.getParentResourceNo());
        //如果父节点是禁用,子节点要修改成启用,抛出异常
        //如果父节点是启用,子节点要修改成禁用,将子节点的所有子节点改为禁用,再修改
        //如果父节点是启用,子节点要修改成启用,将子节点的所有子节点改为启用,再修改
        String dataState = parentResource.getDataState();
        if (StrUtil.equals(dataState, SuperConstant.DATA_STATE_1) && resourceDto.getDataState().equals(SuperConstant.DATA_STATE_0)) {
            throw new BaseException(BasicEnum.PARENT_DEPT_DISABLE);
        }
        updateResourceDataState(resource);
    }

    private void updateResourceDataState(Resource resource) {
        resourceMapper.updateResourceDataState(resource);
        List<Resource> childResourceList = resourceMapper.findChildResourceByParentResourceNo(resource.getResourceNo());
        if (CollectionUtil.isNotEmpty(childResourceList)) {
            childResourceList.forEach(childResource -> {
                childResource.setDataState(resource.getDataState());
                updateResourceDataState(childResource);
            });
        }
    }

    @Override
    public void deleteResource(String menuId) {
        Resource resource = resourceMapper.selectResourceByResourceNo(menuId);
        if (!StrUtil.equals(resource.getDataState(), SuperConstant.DATA_STATE_1)){
            throw new BaseException(BasicEnum.ENABLED_CANNOT_DELETED);
        }
        List<Resource> childResourceList = resourceMapper.findChildResourceByParentResourceNo(resource.getResourceNo());
        if (CollectionUtil.isNotEmpty(childResourceList)){
            throw new BaseException(BasicEnum.EXIT_CHILD_CANNOT_DELETE);
        }
        resourceMapper.deleteResource(menuId);
    }

    private String createResourceNo(String parentResourceNo) {
        //3.1 查询子菜单列表
        List<Resource> childrenList = resourceMapper.findChildList(parentResourceNo);
        childrenList = childrenList.stream().filter(resource -> StrUtil.equals(resource.getResourceType(), SuperConstant.MENU)).collect(Collectors.toList());
        //3.2 生成资源编号
        String resourceNo = null;
        if (CollectionUtil.isEmpty(childrenList)) {
            //3.2.1 如果当前父菜单没有子菜单 , 在父菜单编号基础上增加即可
            resourceNo = NoProcessing.createNo(parentResourceNo, false);
        } else {
            //3.2.2 如果当前父菜单存在子菜单 , 找到编号最大的子菜单, 在其基础上增加
            Long maxNo = childrenList.stream()
                    // 将流中的菜单对象转化为Long类型的菜单编号
                    .map(children -> Long.valueOf(children.getResourceNo()))
                    //取编号的最大值
                    .max(Comparator.comparing(id -> id))
                    .get();
            resourceNo = NoProcessing.createNo(String.valueOf(maxNo), true);
        }

        return resourceNo;
    }
}
